﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Linq;

namespace Server
{

    public class Program
    {
        private static Dictionary<string, Action<string, string>> ThisRule = new Dictionary<string, Action<string, string>>();
        private static Dictionary<string, Socket> IpValues = new Dictionary<string, Socket>();//当前连接的客户端
        private static Socket tcpServer;//服务器socket
        private static string Ip;
        private static int MaxListen = 5;//最大连接数
        private static List<string> allName = new List<string> { "小王", "小李", "小刘", "小明", "小花" };
        private static List<string> occupyName = new List<string>();
        public const string system = "服务器";

        public static void Main(string[] args)
        {
            Ip = GetLocalIP();
            tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPAddress iP = IPAddress.Parse(Ip);
            IPEndPoint iPEndPoint = new IPEndPoint(iP, 6789);//申请端口
            tcpServer.Bind(iPEndPoint);

            tcpServer.Listen(MaxListen);//最大连接数量
            Thread myThread = new Thread(StratSend);//开启一个线程进行监听
            myThread.IsBackground = true;
            myThread.Start();
            ThisRule.Add("All", SendAll);
            while (true)
            {
                UserRead();
            }
        }
        public static void UserRead()
        {
            string str = Console.ReadLine();
            string[] values = str.Split('|');
            if (IpValues.Count == 0)
            {
                Console.WriteLine("当前没有用户连接");
                return;
            }
            if (!IsLegal(str, (a, x) => { if (ThisRule.ContainsKey(a)) ThisRule[a].Invoke(x, system); }))
            {
                Console.WriteLine("消息格式不合法，请重新输入!");
            }
        }

        public static void SendAll(string str, string sender = "")
        {
            foreach (var item in IpValues)
            {
                Send((string.IsNullOrEmpty(str) ? system : sender) + "世界广播", item.Value, str);
            }
        }

        public static void StratSend()
        {
            Console.WriteLine("服务器已开启，输入格式如下\n私发客户端:Name|内容\n群发客户端:All|内容");
            while (true)
            {
                Socket clientSocket = tcpServer.Accept();//暂停当前线程 直到有客户端连接，开始执行下面的代码

                for (int i = 0; i < allName.Count; i++)
                {
                    string str = allName[i];
                    if (!occupyName.Contains(str))
                    {
                        //IPAddress remote_ip = ((IPEndPoint)clientSocket.RemoteEndPoint).Address;
                        IpValues.Add(str, clientSocket);//添加到已连接ip
                        occupyName.Add(str);
                        ThisRule.Add(str, (a, y) => Send(str, clientSocket, a));
                        Thread thread = new Thread(() => Monitor(str, clientSocket));//开启一个线程监听消息
                        thread.IsBackground = true;
                        thread.Start();
                        Console.WriteLine("{0}连接成功，当前连接数{1}", str, IpValues.Count);
                        Send(system, clientSocket, string.Format("你已成功连接服务器,你被服务器分配的名字是“{0}”", allName[i]));
                        break;
                    }
                }

            }
        }
        /// <summary>
        /// 对指定socket发送消息
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="value">抄送人+内容</param>
        public static void Send(string sender, Socket socket, string value)
        {
            string str = string.Format("{0}:{1}", sender, value);
            //把字符传变成流
            byte[] by = Encoding.UTF8.GetBytes(str);
            //发送给客户端
            socket.Send(by);
        }
        /// <summary>
        /// 接收消息
        /// </summary>
        /// <param name="socket"></param>
        public static void Monitor(string name, Socket socket)
        {
            while (true)
            {
                try
                {
                    byte[] strs = new byte[(2048 * 2048)];
                    int a = socket.Receive(strs);
                    string str = Encoding.UTF8.GetString(strs, 0, a);
                    if (IsLegal(str, (x, y) => ThisRule[x].Invoke(y, name)))
                        Send(system, socket, "消息发送成功");
                    else
                        Send(system, socket, "消息发送失败,输入格式不正确或对方不在线");
                    Console.WriteLine(string.Format("{0}输入了指令:{1}", name, str));
                }
                catch
                {
                    Console.WriteLine(name + "退出连接");
                    occupyName.Remove(name);
                    IpValues.Remove(name);
                    ThisRule.Remove(name);
                    Console.WriteLine("当前客户端连接数{0}", IpValues.Count);
                    break;
                }
            }
        }
        public static bool IsLegal(string str, Action<string, string> action)
        {
            string[] values = str.Split('|');
            if (values.Length > 2 || !ThisRule.ContainsKey(values[0]))
            {
                return false;
            }
            else if (values.Length > 1)
            {
                action.Invoke(values[0], values[1]);
                return true;
            }
            else
            {
                return false;
            }
        }


        /// <summary>
        /// 获取本机IP地址
        /// </summary>
        /// <returns>本机IP地址</returns>
        public static string GetLocalIP()
        {
            try
            {
                string HostName = Dns.GetHostName(); //得到主机名
                IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
                for (int i = 0; i < IpEntry.AddressList.Length; i++)
                {
                    //从IP地址列表中筛选出IPv4类型的IP地址
                    //AddressFamily.InterNetwork表示此IP为IPv4,
                    //AddressFamily.InterNetworkV6表示此地址为IPv6类型
                    if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
                    {
                        Console.WriteLine(IpEntry.AddressList[i].ToString());
                        return IpEntry.AddressList[i].ToString();
                    }
                }
                return "";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }
    }
}
